home *** CD-ROM | disk | FTP | other *** search
/ PC World Interactive 11 / PC World Interactive 11.iso / share / convert / ppmtoxpm.lzh / PPMTOXPM.C < prev    next >
C/C++ Source or Header  |  1994-08-19  |  15KB  |  411 lines

  1. /* ppmtoxpm.c - read a portable pixmap and produce a X11 pixmap
  2. **
  3. ** Copyright (C) 1990 by Mark W. Snitily
  4. **
  5. ** Permission to use, copy, modify, and distribute this software and its
  6. ** documentation for any purpose and without fee is hereby granted, provided
  7. ** that the above copyright notice appear in all copies and that both that
  8. ** copyright notice and this permission notice appear in supporting
  9. ** documentation.  This software is provided "as is" without express or
  10. ** implied warranty.
  11. **
  12. ** This tool was developed for Schlumberger Technologies, ATE Division, and
  13. ** with their permission is being made available to the public with the above
  14. ** copyright notice and permission notice.
  15. **
  16. */
  17.  
  18. #include <stdio.h>
  19. #include <ctype.h>
  20. #include "ppm.h"
  21. #include "ppmcmap.h"
  22.  
  23. #ifdef SYSV
  24. #include <string.h>
  25. #define index strchr
  26. #else /*SYSV*/
  27. #include <strings.h>
  28. #endif /*SYSV*/
  29.  
  30. /* Max number of colors allowed in ppm input. */
  31. #define MAXCOLORS    256
  32.  
  33. /* Max number of rgb mnemonics allowed in rgb text file. */
  34. #define MAX_RGBNAMES 1024
  35.  
  36. /* Lower bound and upper bound of character-pixels printed in XPM output.
  37.    Be careful, don't want the character '"' in this range. */
  38. /*#define LOW_CHAR  '#'  <-- minimum ascii character allowed */
  39. /*#define HIGH_CHAR '~'  <-- maximum ascii character allowed */
  40. #define LOW_CHAR  '`'
  41. #define HIGH_CHAR 'z'
  42.  
  43. #define max(a,b) ((a) > (b) ? (a) : (b))
  44.  
  45. void read_rgb_names();  /* forward reference */
  46. void gen_cmap();        /* forward reference */
  47.  
  48. typedef struct {  /* rgb values and ascii names (from rgb text file) */
  49.    int  r, g, b;  /* rgb values, range of 0 -> 65535 */
  50.    char *name;    /* color mnemonic of rgb value */
  51. } rgb_names;
  52.  
  53. typedef struct {  /* character-pixel mapping */
  54.    char *cixel;   /* character string printed for pixel */
  55.    char *rgbname; /* ascii rgb color, either color mnemonic or #rgb value */
  56. } cixel_map;
  57.  
  58. pixel **pixels;
  59.  
  60. main(argc, argv)
  61. int argc;
  62. char *argv[];
  63. {
  64.    FILE *ifd;
  65.    register pixel *pP;
  66.    int argn, rows, cols, ncolors, row, col, i;
  67.    pixval maxval;  /* pixval == unsigned char or unsigned short */
  68.    colorhash_table  cht;
  69.    colorhist_vector chv;
  70.  
  71.    /* Used for rgb value -> rgb mnemonic mapping */
  72.    int map_rgb_names = 0;
  73.    rgb_names rgbn[MAX_RGBNAMES];
  74.    int rgbn_max;
  75.  
  76.    /* Used for rgb value -> character-pixel string mapping */
  77.    cixel_map cmap[MAXCOLORS];
  78.    int charspp; /* chars per pixel */
  79.  
  80.    char out_name[100], rgb_fname[100], *cp;
  81.    char *usage = "[-name <xpm-name>] [-rgb <rgb-textfile>] [ppmfile]";
  82.  
  83.    pm_progname = argv[0];
  84.    out_name[0] = rgb_fname[0] = '\0';
  85.  
  86.    argn = 1;
  87.  
  88.    /* Check for command line options. */
  89.    while (argn < argc && argv[argn][0] == '-') {
  90.  
  91.       /* Case "-", use stdin for input. */
  92.       if (argv[argn][1] == '\0') break;
  93.  
  94.       /* Case "-name <xpm-filename>", get output filename. */
  95.       if (strncmp(argv[argn], "-name", max(strlen(argv[argn]),2)) == 0) {
  96.          argn++;
  97.          if (argn == argc || sscanf(argv[argn], "%s", out_name) != 1)
  98.             pm_usage(usage);
  99.       }
  100.  
  101.       /* Case "-rgb <rgb-filename>", get rgb mnemonics filename. */
  102.       else if (strncmp(argv[argn], "-rgb", max(strlen(argv[argn]),2)) == 0) {
  103.          argn++;
  104.          if (argn == argc || sscanf(argv[argn], "%s", rgb_fname) != 1)
  105.             pm_usage(usage);
  106.          map_rgb_names = 1;
  107.       }
  108.  
  109.       /* Nothing else allowed... */
  110.       else
  111.          pm_usage(usage);
  112.  
  113.       argn++;
  114.    }
  115.  
  116.    /* Input file specified, open it and set output filename if necessary. */
  117.    if (argn < argc) {
  118.  
  119.       /* Open the input file. */
  120.       ifd = pm_openr(argv[argn]);
  121.  
  122.       /* If output filename not specified, use input filename as default. */
  123.       if (out_name[0] == '\0') {
  124.          strcpy(out_name, argv[argn]);
  125.          if (cp = index(out_name, '.')) *cp = '\0'; /* remove extension */
  126.       }
  127.  
  128.       /* If (1) input file was specified as "-" we're using stdin, or
  129.             (2) output filename was specified as "-",
  130.          set output filename to the default. */
  131.       if (!strcmp(out_name, "-" )) strcpy(out_name, "noname");
  132.  
  133.       argn++;
  134.    }
  135.  
  136.    /* No input file specified.  Using stdin so set default output filename. */
  137.    else {
  138.       ifd = stdin;
  139.       if (out_name[0] == '\0') strcpy(out_name, "noname");
  140.    }
  141.  
  142.    /* Only 0 or 1 input files allowed. */
  143.    if (argn != argc) pm_usage(usage);
  144.  
  145.    /* "maxval" is the largest value that can be be found in the ppm file.
  146.       All pixel components are relative to this value. */
  147.    pixels = ppm_readppm(ifd, &cols, &rows, &maxval);
  148.    pm_close(ifd);
  149.  
  150.    /* Figure out the colormap. */
  151.    fprintf(stderr, "(Computing colormap...");  fflush(stderr);
  152.    chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &ncolors);
  153.    if (chv == (colorhist_vector) 0)
  154.       pm_error(
  155.          "too many colors - try running the pixmap through 'ppmquant 256'",
  156.           0, 0, 0, 0, 0);
  157.    fprintf(stderr, "  Done.  %d colors found.)\n", ncolors);
  158.  
  159.    /* Make a hash table for fast color lookup. */
  160.    cht = ppm_colorhisttocolorhash(chv, ncolors);
  161.  
  162.    /* If a rgb text file was specified, read in the rgb mnemonics.
  163.       Does not return if fatal error occurs. */
  164.    if (map_rgb_names) read_rgb_names(rgb_fname, rgbn, &rgbn_max);
  165.  
  166.    /* Now generate the character-pixel colormap table. */
  167.    gen_cmap(chv, ncolors, maxval, map_rgb_names, rgbn, rgbn_max,
  168.             cmap, &charspp);
  169.  
  170.    /* Write out the XPM header. */
  171.    printf("#define %s_format %d\n",          out_name, 1);
  172.    printf("#define %s_width  %d\n",          out_name, cols);
  173.    printf("#define %s_height %d\n",          out_name, rows);
  174.    printf("#define %s_ncolors %d\n",         out_name, ncolors);
  175.    printf("#define %s_chars_per_pixel %d\n", out_name, charspp);
  176.  
  177.    /* Write out the ascii colormap. */
  178.    printf("static char *%s_colors[] = {\n", out_name);
  179.    for (i=0; i<ncolors; i++) {
  180.       printf("   \"%s\", \"%s\"", cmap[i].cixel, cmap[i].rgbname);
  181.       if (i != (ncolors - 1)) printf(",\n");
  182.       else                    printf("\n"); 
  183.    }     
  184.    printf("};\n");
  185.  
  186.    /* Write out the ascii character-pixel image. */
  187.    printf("static char *%s_pixels[] = {\n", out_name);
  188.    for (row=0;  row<rows;  row++) {
  189.       printf("\"");
  190.       for (col=0, pP=pixels[row];  col<cols;  col++, pP++)
  191.          printf("%s", cmap[ppm_lookupcolor(cht, *pP)].cixel);
  192.       if (row != (rows - 1)) printf("\",\n");
  193.       else                   printf("\"\n"); 
  194.    }
  195.    printf("};\n");
  196.  
  197.    exit(0);
  198.  
  199. } /* main */
  200.  
  201. /*---------------------------------------------------------------------------*/
  202. /* This routine reads a rgb text file.  It stores the rgb values (0->65535)
  203.    and the rgb mnemonics (malloc'ed) into the "rgbn" array.  Returns the
  204.    number of entries stored in "rgbn_max". */
  205. void read_rgb_names(rgb_fname, rgbn, rgbn_max)
  206. char *rgb_fname;
  207. rgb_names rgbn[MAX_RGBNAMES];
  208. int *rgbn_max;
  209. {
  210.    FILE *rgbf;
  211.    int i, items, red, green, blue;
  212.    char line[512], name[512], *rgbname, *n, *m;
  213.  
  214.    /* Open the rgb text file.  Abort if error. */
  215.    if ((rgbf = fopen(rgb_fname, "r")) == NULL)
  216.       pm_error("error opening rgb text file \"%s\"", rgb_fname, 0, 0, 0, 0);
  217.  
  218.    /* Loop reading each line in the file. */
  219.    for (i=0;  fgets(line, sizeof(line), rgbf);  i++) {
  220.  
  221.        /* Quit if rgb text file is too large. */
  222.        if (i == MAX_RGBNAMES) {
  223.           fprintf(stderr,
  224.              "Too many entries in rgb text file, truncated to %d entries.\n",
  225.              MAX_RGBNAMES);
  226.           fflush(stderr);
  227.           break;
  228.        }
  229.  
  230.        /* Read the line.  Skip if bad. */
  231.        items = sscanf(line, "%d %d %d %[^\n]\n", &red, &green, &blue, name);
  232.        if (items != 4) {
  233.           fprintf(stderr, "rgb text file syntax error on line %d\n", i+1);
  234.           fflush(stderr);
  235.           i--;
  236.           continue;
  237.        }
  238.  
  239.        /* Make sure rgb values are within 0->255 range.  Skip if bad. */
  240.        if (red   < 0 || red   > 0xFF ||
  241.            green < 0 || green > 0xFF ||
  242.            blue  < 0 || blue  > 0xFF) {
  243.           fprintf(stderr, "rgb value for \"%s\" out of range, ignoring it\n",
  244.                   name);
  245.           fflush(stderr);
  246.           i--;
  247.           continue;
  248.        }
  249.  
  250.        /* Allocate memory for ascii name.  Abort if error. */
  251.        if (!(rgbname = (char *) malloc(strlen(name)+1)))
  252.           pm_error("out of memory allocating rgb name", 0, 0, 0, 0, 0);
  253.  
  254.        /* Copy string to ascii name and lowercase it. */
  255.        for (n=name, m=rgbname; *n; n++)
  256.           *m++ = isupper(*n) ? tolower(*n) : *n;
  257.        *m = '\0';
  258.  
  259.        /* Save the rgb values and ascii name in the array. */
  260.        rgbn[i].r = red   << 8;
  261.        rgbn[i].g = green << 8;
  262.        rgbn[i].b = blue  << 8;
  263.        rgbn[i].name = rgbname;
  264.    }
  265.  
  266.    /* Return the max number of rgb names. */
  267.    *rgbn_max = i-1;
  268.  
  269.    fclose(rgbf);
  270.  
  271. } /* read_rgb_names */
  272.  
  273. /*---------------------------------------------------------------------------*/
  274. /* Given a number and a base, (base == HIGH_CHAR-LOW_CHAR+1), this routine
  275.    prints the number into a malloc'ed string and returns it.  The length of
  276.    the string is specified by "digits".  The ascii characters of the printed
  277.    number range from LOW_CHAR to HIGH_CHAR.  The string is LOW_CHAR filled,
  278.    (e.g. if LOW_CHAR==0, HIGH_CHAR==1, digits==5, i=3, routine would return
  279.    the malloc'ed string "00011"). */
  280. char *gen_numstr(i, base, digits)
  281. int i, base, digits;
  282. {
  283.    char *str, *p;
  284.    int d;
  285.  
  286.    /* Allocate memory for printed number.  Abort if error. */
  287.    if (!(str = (char *) malloc(digits+1)))
  288.       pm_error("out of memory", 0, 0, 0, 0, 0);
  289.  
  290.    /* Generate characters starting with least significant digit. */
  291.    p = str + digits;
  292.    *p-- = '\0'; /* nul terminate string */
  293.    while (p >= str) {
  294.       d = i % base;
  295.       i /= base;
  296.       *p-- = (char) ((int) LOW_CHAR + d);
  297.    }
  298.  
  299.    return str;
  300.  
  301. } /* gen_numstr */
  302.  
  303. /*---------------------------------------------------------------------------*/
  304. /* This routine generates the character-pixel colormap table. */
  305. void gen_cmap(chv, ncolors, maxval, map_rgb_names, rgbn, rgbn_max,
  306.               cmap, charspp)
  307. /* input: */
  308. colorhist_vector chv;         /* contains rgb values for colormap */
  309. int       ncolors;            /* number of entries in colormap */
  310. pixval    maxval;             /* largest color value, all rgb values relative
  311.                                  to this, (pixval == unsigned short) */
  312. int       map_rgb_names;      /* == 1 if mapping rgb values to rgb mnemonics */
  313. rgb_names rgbn[MAX_RGBNAMES]; /* rgb mnemonics from rgb text file */
  314. int       rgbn_max;           /* number of rgb mnemonics in table */
  315. /* output: */
  316. cixel_map cmap[MAXCOLORS];    /* pixel strings and ascii rgb colors */
  317. int       *charspp;           /* characters per pixel */
  318. {
  319.    int i, j, base, cpp, mval, red, green, blue, r, g, b, matched;
  320.    char *str;
  321.  
  322.    /* Figure out how many characters per pixel we'll be using.  Don't want
  323.       to be forced to link with libm.a, so using a division loop rather than
  324.       a log function. */
  325.    base = (int) HIGH_CHAR - (int) LOW_CHAR + 1;
  326.    for (cpp=0, j=ncolors;  j;  cpp++) j /= base;
  327.    *charspp = cpp;
  328.  
  329.    /* Determine how many hex digits we'll be normalizing to if the rgb value
  330.       doesn't match a color mnemonic. */
  331.    mval = (int) maxval;
  332.    if      (mval <= 0x000F) mval = 0x000F;
  333.    else if (mval <= 0x00FF) mval = 0x00FF;
  334.    else if (mval <= 0x0FFF) mval = 0x0FFF;
  335.    else                     mval = 0xFFFF;
  336.  
  337.    /* Generate the character-pixel string and the rgb name for each colormap
  338.       entry. */
  339.    for (i=0; i<ncolors; i++) {
  340.  
  341.       /* The character-pixel string is simply a printed number in base "base"
  342.          where the digits of the number range from LOW_CHAR to HIGH_CHAR and
  343.          the printed length of the number is "cpp". */
  344.       cmap[i].cixel = gen_numstr(i, base, cpp);
  345.  
  346.       /* Fetch the rgb value of the current colormap entry. */
  347.       red   = PPM_GETR(chv[i].color);
  348.       green = PPM_GETG(chv[i].color);
  349.       blue  = PPM_GETB(chv[i].color);
  350.  
  351.       /* If the ppm color components are not relative to 15, 255, 4095, 65535,
  352.          normalize the color components here. */
  353.       if (mval != (int) maxval) {
  354.          red   = (red   * mval) / (int) maxval;
  355.          green = (green * mval) / (int) maxval;
  356.          blue  = (blue  * mval) / (int) maxval;
  357.       }
  358.  
  359.       /* If the "-rgb <rgbfile>" option was specified, attempt to map the
  360.          rgb value to a color mnemonic. */
  361.       if (map_rgb_names) {
  362.  
  363.          /* The rgb values of the color mnemonics are normalized relative
  364.             to 255 << 8, (i.e. 0xFF00).  [That's how the original MIT code
  365.             did it, really should have been "v * 65535 / 255" instead of
  366.             "v << 8", but have to use the same scheme here or else colors
  367.             won't match...]  So, if our rgb values aren't already 16-bit
  368.             values, need to shift left. */ 
  369.          if      (mval==0x000F) { r=red << 12;  g=green << 12;  b=blue << 12;
  370.                                   /* Special case hack for "white". */
  371.                                   if (0xF000 == r && r == g && g == b)
  372.                                      r = g = b = 0xFF00;
  373.                                                                              }
  374.          else if (mval==0x00FF) { r=red <<  8;  g=green <<  8;  b=blue <<  8;}
  375.          else if (mval==0x0FFF) { r=red <<  4;  g=green <<  4;  b=blue <<  4;}
  376.          else                   { r=red;        g=green;        b=blue;      }
  377.  
  378.          /* Just perform a dumb linear search over the rgb values of the color
  379.             mnemonics.  One could speed things up by sorting the rgb values
  380.             and using a binary search, or building a hash table, etc... */
  381.          for (matched = 0, j=0; j <= rgbn_max; j++)
  382.              if (r == rgbn[j].r && g == rgbn[j].g && b == rgbn[j].b) {
  383.  
  384.                 /* Matched.  Allocate string, copy mnemonic, and exit. */
  385.                 if (!(str = (char *) malloc(strlen(rgbn[i].name)+1)))
  386.                    pm_error("out of memory", 0, 0, 0, 0, 0);
  387.                 strcpy(str, rgbn[j].name);
  388.                 cmap[i].rgbname = str;
  389.                 matched = 1;
  390.                 break;
  391.              }
  392.          if (matched) continue;
  393.       }
  394.  
  395.       /* Either not mapping to color mnemonics, or didn't find a match.
  396.          Generate an absolute #RGB value string instead. */
  397.       if (!(str = (char *) malloc(mval == 0x000F ?  5 :
  398.                                   mval == 0x00FF ?  8 :
  399.                                   mval == 0x0FFF ? 11 :
  400.                                                    14)))
  401.          pm_error("out of memory", 0, 0, 0, 0, 0);
  402.  
  403.       sprintf(str, mval == 0x000F ? "#%X%X%X"       :
  404.                    mval == 0x00FF ? "#%02X%02X%02X" :
  405.                    mval == 0x0FFF ? "#%03X%03X%03X" :
  406.                                     "#%04X%04X%04X", red, green, blue);
  407.       cmap[i].rgbname = str;
  408.    }
  409.  
  410. } /* gen_cmap */
  411.